/*****************************************************************************
* if_sio.c - serial device driver
*
* Copyright (c) 2001 by Cognizant Pty Ltd.
*
* The authors hereby grant permission to use, copy, modify, distribute,
* and license this software and its documentation for any purpose, provided
* that existing copyright notices are retained in all copies and that this
* notice and the following disclaimer are included verbatim in any 
* distributions. No written agreement, license, or royalty fee is required
* for any of the authorized uses.
*
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
* REVISION HISTORY (please don't use tabs!)
*
*(yyyy-mm-dd)
* 2001-12-29 Robert Dickenson <odin@pnc.com.au>, Cognizant Pty Ltd.
*            Original file.
*
******************************************************************************
*/
/*
#include "..\..\netconf.h"
#include "..\..\netbuf.h"
#include "..\..\netos.h"
#include "..\..\netifdev.h"
#include <stdio.h>
#include "..\..\netdebug.h"
 */
#include "netconf.h"
#include "netbuf.h"
#include "netos.h"
#include "netifdev.h"
#include <stdio.h>
#include <string.h>
#include "netdebug.h"
#include "if_sio.h"

#include "sioport.h"

int nBaudRate = 9600;
int nPortNum = 1;

/***********************************************************************************
 *
 * Function:    start(void)
 * Description: This function initializes the serial port to work in the mode 
 *              desired for this application.
 *
 **********************************************************************************/
static unsigned char start(void)
{
    TRACE("IF_SIO:start()\n");

    printf("Attempting to open COM%u at %u.\n", nPortNum, nBaudRate);
    if (!Open(nPortNum, nBaudRate)) {
        printf("ERROR: Failed to open serial port COM%u.\n", nPortNum);
        return 1;
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
// 
//
static unsigned char stop(void)
{
    TRACE("IF_SIO:stop()\n");
    if (IsOpened()) {
        if (!Close()) {
            printf("ERROR: Failed to close serial port.\n");
            return 1;
        }
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
// send complete frame from NBuf to device and transmit.
//
static unsigned char transmit(NBuf* pNBuf)
{
    int count = 0;

//    u_short packet_len;
//    packet_len = nChainLen(pNBuf);
//    TRACE("transmit(...) nChainLen - %u\n", packet_len);

    NBuf* nb = pNBuf;
    char* sPtr;
    short n;
//    nb->nextBuf = NULL; // TODO: debug why this is req.
    while (nb) {
        sPtr = nBUFTOPTR(nb, char*);
        n = nb->len;
        count = SendData(sPtr, nb->len);
        if (count != nb->len) {
            printf("ERROR: transmit(...) not all data sent\n.");
        } else {
//            char buffer[200];
//            strncpy(buffer, sPtr, count);
//            buffer[count] = '\0';
//            printf("XMIT: %s\n", buffer);
        }
        nb = nb->nextBuf;
    }
    return 0;
}


////////////////////////////////////////////////////////////////////////////////
// Serial Port device interrupt support for frame reception.
//
#define MAIN_BUFLEN 50
char buffer[MAIN_BUFLEN];

////////////////////////////////////////////////////////////////////////////////
/* 
 * NBufPut - append given character to end of given nBuf.
 * If nBuf is full, append another.
 * Return the current nBuf.
 */
static NBuf* NBufPut(u_char c, NBuf* nb)
{
    NBuf* tb = nb;
    
    // Make sure there is room for the character and an escape code.
    // Sure we don't quite fill the buffer if the character doesn't
    // get escaped but is one character worth complicating this?
    // Note: We assume no packet header.
    if (nb && (&nb->body[NBUFSZ] - (nb->data + nb->len)) < 1) {
        nGET(tb);
        if (tb) {
            nb->nextBuf = tb;
            tb->len = 0;
        }
        nb = tb;
    }
    if (nb) {
        *(nb->data + nb->len++) = c;
    }
    return tb;
}

static NBuf* receive(void)
{
    NBuf* headNB = NULL;
    u_short packet_len = 0;

    int bytes = 0;
    bytes = ReadDataWaiting();
    packet_len = bytes;

    OS_ENTER_CRITICAL();
    nGET(headNB);           // Grab an input buffer.
    OS_EXIT_CRITICAL();

//    TRACE("receive() packet_len: %u\n", packet_len);
    if (headNB != NULL) {
        u_short i;
        NBuf* tailNB = headNB;
        u_short wordcnt = packet_len >> 1;
        headNB->len = 0;
        if (bytes > 0) {
            int count = ReadData(buffer, min(bytes, MAIN_BUFLEN));
            buffer[count] = '\0';
//            printf("RECV: %s\n", buffer);
            for (i = 0; i < count; i++) {
                unsigned char data = buffer[i];
                tailNB = NBufPut(data, tailNB);
                if (tailNB == NULL) {
                    TRACE("receive() error building nBuf chain ! - nFreeChain(%p)\n", headNB);
                    nFreeChain(headNB);
                    return (headNB = NULL);
                }
            }
/*
            int len;
            nAPPEND(headNB, (void*)data, count, len);
            if (len != count) {
                nFreeChain(headNB);
                return (headNB = NULL);
            }
 */
        }
    } else {
        TRACE("receive() - failed to nGET()\n");
    }
    return headNB;
}

/***********************************************************************************
 *
 * Function:    ProcessISQ(void)
 * Description: Here we process the interrupt.
 *
 **********************************************************************************/
#ifdef QUEUE_TASKS
void ProcessISQ(Interface* pInterface)
{
    NBuf* headNB = NULL;
    if (pInterface != NULL {
        headNB = receive();      // Go handle it
        if (headNB != NULL)
            OSQPost(pInterface->pRxQ, headNB);
    }
}
#else
void ProcessISQ(Interface* pInterface)
{
    if (pInterface != NULL) {
        pInterface->rxEventCnt++;
        OSSemPost(pInterface->pSemIF);
    }
//            pInterface->txEventCnt++;
//            OSSemPost(pInterface->pSemIF);
}
#endif

void interrupt_proc(Interface* pInterface)
{
}

////////////////////////////////////////////////////////////////////////////////
/*
1.  Declare/create new device structure
2.  Call DriverEntry(&device) to initialise func pointers
3.  
 */

static unsigned char statistics(if_statistics* pStats)
{
    return FALSE;
}

static unsigned char receive_ready(void)
{
    return ReadDataWaiting();
}

static unsigned char dummy_func(void)
{
    return TRUE;
}

unsigned char SIO_DriverEntry(Interface* pInterface)
{
    pInterface->start = start;
    pInterface->stop = stop;
    pInterface->receive = receive;
    pInterface->receive_ready = receive_ready;
    pInterface->transmit = transmit;
    pInterface->transmit_ready = dummy_func;
    pInterface->statistics = statistics;
//    pInterface->interrupt = ProcessISQ;
    pInterface->interrupt = interrupt_proc;
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
